#pragma once
#include <cstdarg>
#include <typeinfo>
#include <map>
#include <sfse_common/Utilities.h>

#include "Addresses.h"
#include "ScaleformGFxMovie.h"
#include "ScaleformGFxTypes.h"
#include "sfse/ScaleformValue.h"
#include "sfse_common/Types.h"


namespace Scaleform
{
    namespace GFx
    {
        class FunctionHandler : public RefCountBase<FunctionHandler>
        {
        public:
            FunctionHandler();
            virtual ~FunctionHandler();

            // 38
            class Params
            {
            public:
                Value* pRetVal;
                Movie* pMovie;
                Value* pThis;
                Value* pArgsWithThisRef;
                Value* pArgs;
                u32 ArgCount;
                void* pUserData; // optionId
            };

            virtual void Call(Params* params) = 0;
        };
    }
}

class BSGFxFunctionHandler : public Scaleform::GFx::FunctionHandler
{
public:
    virtual ~BSGFxFunctionHandler()
    {
    };
};

class SWFToCodeFunctionHandler : public Scaleform::GFx::FunctionHandler
{
public:
    virtual ~SWFToCodeFunctionHandler()
    {
    }

    virtual void MapCodeObjectFunctions() = 0; // 02

    DEFINE_MEMBER_FN_2(MapCodeMethodToASFunction, void, MapCodeMethodToASFunction_ADDRESS, const char * name, u32 index);
};

typedef std::map<const std::type_info *, Scaleform::GFx::FunctionHandler *>	FunctionHandlerCache;
extern FunctionHandlerCache g_functionHandlerCache;

template <typename T>
void CreateFunction(Scaleform::GFx::Value * dst, Scaleform::GFx::AS3::MovieRoot * movie)
{
    // either allocate the object or retrieve an existing instance from the cache
    Scaleform::GFx::FunctionHandler	* fn = nullptr;

    // check the cache
    FunctionHandlerCache::iterator iter = g_functionHandlerCache.find(&typeid(T));
    if(iter != g_functionHandlerCache.end())
        fn = iter->second;

    if(!fn)
    {
        // not found, allocate a new one
        fn = new T;

        // add it to the cache
        // cache now owns the object as far as refcounting goes
        g_functionHandlerCache[&typeid(T)] = fn;
    }

    // create the function object
    movie->CreateFunction(dst, fn);
}

template <typename T>
void RegisterFunction(Scaleform::GFx::Value * dst, Scaleform::GFx::AS3::MovieRoot * movie, const char * name)
{
    // either allocate the object or retrieve an existing instance from the cache
    Scaleform::GFx::Value fnValue;
    CreateFunction<T>(&fnValue, movie);
    dst->SetMember(name, &fnValue);
}
